home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 October / Macworld (1998-10).dmg / Shareware World / Info / For Developers / MacZoop 1.8.4 / More Classes / Window Classes / ZTextWindow.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-07-07  |  13.4 KB  |  748 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            ObjectMacZapp        -- a standard Mac OOP application template
  5. *
  6. *
  7. *
  8. *            ZTextWindow.cpp        -- a window that displays text files (uses TextEdit)
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            © 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21.  
  22.  
  23. #include    "ZTextWindow.h"
  24. #include    "MacZoop.h"
  25.  
  26. #include    <TextEdit.h>
  27.  
  28. static pascal Boolean    ztClickproc();
  29. static ZTextWindow*        gClickedTE;
  30. static TEClickLoopUPP    gTEClickLoopCallBack = NewTEClickLoopProc( ztClickproc );
  31.  
  32.  
  33. CLASSCONSTRUCTOR( ZTextWindow );
  34.  
  35.  
  36. ZTextWindow::ZTextWindow( ZCommander* aBoss, short windID, Boolean allowEditing )
  37.     : ZScroller( aBoss, windID, TRUE, TRUE )
  38. {
  39.     itsText = NULL;
  40.     isEditable = allowEditing;
  41.     wControl = teWindowWidth;
  42.     emWidth = 255;
  43. }
  44.  
  45.  
  46. ZTextWindow::ZTextWindow()
  47.     : ZScroller()
  48. {
  49.     itsText = NULL;
  50.     isEditable = FALSE;
  51.     wControl = teWindowWidth;
  52.     emWidth = 255;
  53. }
  54.  
  55.  
  56.  
  57. ZTextWindow::~ZTextWindow()
  58. {
  59.     if ( itsText )
  60.         TEDispose( itsText );
  61. }
  62.  
  63.  
  64. void    ZTextWindow::InitZWindow()
  65. {
  66.     ZScroller::InitZWindow();
  67.     
  68.     MakeTextEdit();
  69. }
  70.  
  71.  
  72. void    ZTextWindow::DrawContent()
  73. {
  74.     Rect    r;
  75.     
  76.     SetOrigin( 0, 0 );
  77.     GetContentRect( &r );
  78.     ClipRect( &r );
  79.     
  80.     TEUpdate( &r, itsText );
  81. }
  82.  
  83.  
  84. void    ZTextWindow::ClickContent( const Point mouse, const short modifiers )
  85. {
  86.     if ( isEditable )
  87.     {
  88.         Point tm = mouse;
  89.         
  90.         SetOrigin( 0, 0 );
  91.  
  92.         if ( hasVBar )
  93.             tm.v -= GetControlValue( theVBar );
  94.         
  95.         if ( hasHBar )
  96.             tm.h -= GetControlValue( theHBar );
  97.         
  98.         gClickedTE = this;    
  99.         ClipRect( &(*itsText)->viewRect );
  100.         TEClick( tm, (modifiers & shiftKey) == shiftKey, itsText );
  101.     }
  102. }
  103.  
  104.  
  105. void    ZTextWindow::Activate()
  106. {
  107.     ZScroller::Activate();
  108.     
  109.     if ( gFontMenuID )
  110.         gMenuBar->EnableCommand( gFontMenuID, 0 );
  111.  
  112.     if ( isEditable )
  113.         TEActivate( itsText );
  114. }
  115.  
  116.  
  117. void    ZTextWindow::Deactivate()
  118. {
  119.     if ( isEditable )
  120.         TEDeactivate( itsText );
  121.  
  122.     if ( gFontMenuID )
  123.         gMenuBar->DisableCommand( gFontMenuID, 0 );
  124.  
  125.     ZScroller::Deactivate();
  126. }
  127.  
  128.  
  129. void    ZTextWindow::Idle()
  130. {
  131.     if ( isEditable )
  132.         TEIdle( itsText );
  133. }
  134.  
  135.  
  136. void    ZTextWindow::AdjustCursor( const Point mouse, const short modifiers )
  137. {
  138.     Rect        content;
  139.     
  140.     if ( isEditable )
  141.     {
  142.         GetContentRect( &content );
  143.         
  144.         if ( PtInRect( mouse, &content ))
  145.             SetCursorShape( iBeamCursor );
  146.         else
  147.             SetCursorShape( 0 );
  148.     }
  149. }
  150.  
  151.  
  152. void    ZTextWindow::UpdateMenus()
  153. {
  154.     if ( isEditable )
  155.     {
  156.         if ((*itsText)->selStart < (*itsText)->selEnd )
  157.         {
  158.             gMenuBar->EnableCommand( kCmdCut );    
  159.             gMenuBar->EnableCommand( kCmdCopy );    
  160.             gMenuBar->EnableCommand( kCmdClear );    
  161.         }
  162.         
  163.         gMenuBar->EnableCommand( kCmdSelectAll );    
  164.         
  165.         // Font/style menus:
  166.         
  167.         TEStyleRunInfo    ri;
  168.         
  169.         TEGetStyleRunInfo( &ri, itsText );
  170.             
  171.         // check sizes, styles & font
  172.         
  173.         gMenuBar->UpdateFontSizeMenu( &ri );
  174.         gMenuBar->UpdateStyleMenu( &ri );
  175.     }
  176.     
  177.     ZScroller::UpdateMenus();
  178. }
  179.  
  180.  
  181. void    ZTextWindow::HandleCommand( const long aCmd )
  182. {
  183.     // handle font size and style commands
  184.     
  185.     TextStyle        newStyle;
  186.     Boolean            wasChanged = TRUE;
  187.     
  188.     switch( aCmd )
  189.     {
  190.         case kCmdPlainText:
  191.             newStyle.tsFace = normal;
  192.             TESetStyle( doFace, &newStyle, true, itsText );
  193.             break;
  194.         
  195.         case kCmdBoldText:
  196.             newStyle.tsFace = bold;
  197.             TESetStyle( doFace + doToggle, &newStyle, true, itsText );
  198.             break;
  199.         
  200.         case kCmdItalicText:
  201.             newStyle.tsFace = italic;
  202.             TESetStyle( doFace + doToggle, &newStyle, true, itsText );
  203.             break;
  204.         
  205.         case kCmdUnderlineText:
  206.             newStyle.tsFace = underline;
  207.             TESetStyle( doFace + doToggle, &newStyle, true, itsText );
  208.             break;
  209.         
  210.         case kCmdOutlineText:
  211.             newStyle.tsFace = outline;
  212.             TESetStyle( doFace + doToggle, &newStyle, true, itsText );
  213.             break;
  214.         
  215.         case kCmdShadowText:
  216.             newStyle.tsFace = shadow;
  217.             TESetStyle( doFace + doToggle, &newStyle, true, itsText );
  218.             break;
  219.         
  220.         case kCmdCondensedText:
  221.             newStyle.tsFace = condense;
  222.             TESetStyle( doFace + doToggle, &newStyle, true, itsText );
  223.             break;
  224.         
  225.         case kCmdExtendedText:
  226.             newStyle.tsFace = extend;
  227.             TESetStyle( doFace + doToggle, &newStyle, true, itsText );
  228.             break;
  229.         
  230.         case kCmdStdFontSize7:
  231.         case kCmdStdFontSize9:
  232.         case kCmdStdFontSize10:
  233.         case kCmdStdFontSize12:
  234.         case kCmdStdFontSize14:
  235.         case kCmdStdFontSize18:
  236.         case kCmdStdFontSize24:
  237.         case kCmdStdFontSize36:
  238.         case kCmdStdFontSize48:
  239.         case kCmdStdFontSize60:
  240.         case kCmdStdFontSize72:
  241.             newStyle.tsSize = aCmd - kStdFontSizeBase;
  242.             TESetStyle( doSize, &newStyle, true, itsText );
  243.             break;
  244.                 
  245.         default:
  246.             ZScroller::HandleCommand( aCmd );
  247.             wasChanged = FALSE;
  248.             break;
  249.     }
  250.     
  251.     if ( wasChanged )
  252.     {
  253.         dirty = TRUE;
  254.         RecalText();
  255.     }
  256. }
  257.  
  258.  
  259.  
  260. void    ZTextWindow::HandleCommand( const short menuID, const short itemID )
  261. {
  262.     if ( menuID == gFontMenuID )        // Found font menu! Select our font:
  263.     {
  264.         Str255        itemName;
  265.         TextStyle    newStyle;
  266.         
  267.         GetMenuItemText( GetMenuHandle( gFontMenuID ), itemID, itemName );
  268.         GetFNum( itemName, &newStyle.tsFont );
  269.         TESetStyle( doFont, &newStyle, true, itsText );
  270.     }
  271.     else
  272.         ZScroller::HandleCommand( menuID, itemID );
  273. }
  274.  
  275.  
  276.  
  277. Boolean    ZTextWindow::CanPasteType()
  278. {
  279.     return isEditable && gClipboard->QueryType( 'TEXT' );
  280. }
  281.  
  282.  
  283. void    ZTextWindow::DoCut()
  284. {
  285.     if ( isEditable )
  286.     {
  287.         gClipboard->Clear();
  288.         TECut( itsText );
  289.         TEToScrap();
  290.         RecalText();
  291.     }
  292. }
  293.  
  294.  
  295. void    ZTextWindow::DoCopy()
  296. {
  297.     if ( isEditable )
  298.     {
  299.         gClipboard->Clear();
  300.         TECopy( itsText );
  301.         TEToScrap();    
  302.     }
  303. }
  304.  
  305.  
  306. void    ZTextWindow::DoPaste()
  307. {
  308.     if ( isEditable )
  309.     {
  310.         TEFromScrap();
  311.         TEPaste( itsText );
  312.         RecalText();
  313.     }
  314. }
  315.  
  316.  
  317. void    ZTextWindow::DoClear()
  318. {
  319.     if ( isEditable )
  320.     {
  321.         TEDelete( itsText );
  322.         RecalText();
  323.     }
  324. }
  325.  
  326.  
  327. void    ZTextWindow::DoSelectAll()
  328. {
  329.     if ( isEditable )
  330.         TESetSelect( 0, 32767, itsText );
  331.  
  332. }
  333.  
  334.  
  335.  
  336. void    ZTextWindow::SetSize( const short width, const short height )
  337. {
  338.     short    ph, pv, nph, npv;
  339.     
  340.     GetPosition( &ph, &pv );
  341.     
  342.     ZScroller::SetSize( width, height, FALSE );
  343.     
  344.     GetPosition( &nph, &npv );
  345.     
  346.     // if variable width, adjust the width to the new window size
  347.     
  348.     if ( wControl == teWindowWidth )
  349.     {
  350.         Rect    r, cr;
  351.         
  352.         r = (*itsText)->destRect;
  353.         GetContentRect( &cr );
  354.         
  355.         r.right = r.left + cr.right - cr.left;
  356.         
  357.         (*itsText)->destRect = r;
  358.         
  359.         Focus();
  360.         InvalRect( &cr );
  361.     }
  362.     
  363.     // if the resize caused a shift in position, we must scroll to correctly position the text
  364.     // in the view. Normally ZScroller does this for us but text views are complicated by the
  365.     // fact that TextEdit handles its own scrolling.
  366.     
  367.     RecalText();
  368.  
  369.     if ( nph != ph ||
  370.          npv != pv )
  371.     {
  372.         TEScroll( ph - nph, pv - npv, itsText );
  373.         Draw();
  374.     }
  375. }
  376.  
  377.  
  378. void    ZTextWindow::Zoom( const short partCode )
  379. {
  380.     short    ph, pv, nph, npv;
  381.     
  382.     GetPosition( &ph, &pv );
  383.     
  384.     ZScroller::Zoom( partCode );
  385.     
  386.     GetPosition( &nph, &npv );
  387.     
  388.     // if variable width, adjust the width to the new window size
  389.     
  390.     if ( wControl == teWindowWidth )
  391.     {
  392.         Rect    r, cr;
  393.         
  394.         r = (*itsText)->destRect;
  395.         GetContentRect( &cr );
  396.         
  397.         r.right = r.left + cr.right - cr.left;
  398.         
  399.         (*itsText)->destRect = r;
  400.     }
  401.     
  402.     // perform the same trick as SetSize...
  403.     
  404.     RecalText();
  405.  
  406.     if ( nph != ph ||
  407.          npv != pv )
  408.         TEScroll( ph - nph, pv - npv, itsText );
  409.     
  410.     Draw();
  411. }
  412.  
  413.  
  414.  
  415. void    ZTextWindow::Scroll( const short dH, const short dV )
  416. {
  417.     TEPinScroll( dH, dV, itsText );
  418. }
  419.  
  420.  
  421.  
  422. void    ZTextWindow::Type( const char theChar, const short modifiers )
  423. {
  424.     if ( isEditable )
  425.     {
  426.         TEKey( theChar, itsText );
  427.         
  428.         dirty = TRUE;
  429.         
  430.         RecalText();
  431.     }
  432. }
  433.  
  434.  
  435. void    ZTextWindow::MakeTextEdit()
  436. {
  437.     Rect        destRect, viewRect;
  438.     FontInfo    fi;
  439.     
  440.     // the dest rect is the scrollbounds, but that hasn't been set yet.
  441.     // the view rect is the content area by default
  442.     
  443.     Focus();
  444.     
  445.     TextFont( kFontIDGeneva );
  446.     TextSize( 12 );
  447.     GetFontInfo( &fi );
  448.     
  449.     emSpace = fi.widMax;
  450.     GetTextViewRect( &viewRect );
  451.     
  452.     destRect = viewRect;
  453.     destRect.right = destRect.left + ( emSpace * emWidth );
  454.     
  455.     FailNIL( itsText = TEStyleNew( &destRect, &viewRect ));
  456.     
  457.     TESetClickLoop( gTEClickLoopCallBack, itsText );
  458.     SetBounds( destRect );
  459.     RecalText();
  460. }
  461.  
  462.  
  463.  
  464. void    ZTextWindow::OpenFile( const OSType fType, Boolean isStationery )
  465. {
  466.     FInfo    fi;
  467.     short    refNum;
  468.     long    pSize;
  469.     Handle    temp = NULL;
  470.  
  471.     if ( macFile.vRefNum != kNoFile )
  472.     {
  473.         FailOSErr( FSpGetFInfo( &macFile, &fi ));
  474.         
  475.         if ( fi.fdType != 'TEXT' )
  476.             FailOSErr( paramErr );
  477.             
  478.         FailOSErr( FSpOpenDF( &macFile, fsCurPerm, &refNum ));
  479.         
  480.         try
  481.         {
  482.             FailOSErr( GetEOF( refNum, &pSize ));
  483.             if ( pSize > kMaxTextSize )
  484.                 FailOSErr( kTextFileTooBigErr );
  485.                 
  486.             FailNIL( temp = NewHandle( pSize ));    
  487.             
  488.             HLock( temp );
  489.             FailOSErr( FSRead( refNum, &pSize, *temp ));
  490.             
  491.             // set the text in text edit
  492.             
  493.             TESetText( *temp, pSize, itsText );
  494.             HUnlock( temp );
  495.             
  496.             DisposeHandle( temp );
  497.             FSClose( refNum );
  498.             
  499.             // see if there is a 'styl' resource:
  500.             
  501.             refNum = FSpOpenResFile( &macFile, fsCurPerm );
  502.             
  503.             if ( refNum > 0 )    // If there's no resFork, no styles to look for:
  504.             {
  505.                 UseResFile( refNum );
  506.                 temp = Get1Resource( 'styl', 128 );
  507.                 
  508.                 if ( temp && ( ResError() == noErr ))
  509.                 {
  510.                     TEUseStyleScrap( 0, pSize, (StScrpHandle) temp, FALSE, itsText );
  511.                     ReleaseResource( temp );
  512.                 }
  513.                 
  514.                 CloseResFile( refNum );
  515.             }
  516.         }
  517.         catch( OSErr err )
  518.         {
  519.             FSClose( refNum );
  520.             
  521.             if ( temp )
  522.             {
  523.                 HUnlock( temp );
  524.                 DisposeHandle( temp );
  525.             }
  526.             throw err;
  527.         }
  528.     }    
  529.     ZScroller::OpenFile( fType, isStationery );
  530.     
  531.     // set up the bounds, etc
  532.     
  533.     RecalText();
  534.     Draw();
  535. }
  536.  
  537.  
  538.  
  539. void    ZTextWindow::SaveFile()
  540. {
  541.     short        refNum;
  542.     long        pSize;
  543.     OSErr        theErr;
  544.     Handle        text = NULL;
  545.     char        tState;
  546.     
  547.     if ( macFile.vRefNum != kNoFile )
  548.     {
  549.         theErr = FSpOpenDF( &macFile, fsCurPerm, &refNum );
  550.         
  551.         if ( theErr == fnfErr )
  552.         {
  553.             FailOSErr( FSpCreate( &macFile, gAppSignature, 'TEXT', 0 ));
  554.             FSpCreateResFile( &macFile, gAppSignature, 'TEXT', 0 );
  555.             FailOSErr( ResError() );
  556.             FailOSErr( FSpOpenDF( &macFile, fsCurPerm, &refNum ));
  557.         }
  558.         else
  559.             FailOSErr( theErr );
  560.         
  561.         try
  562.         {
  563.             FailNIL( text = (*itsText)->hText );
  564.             
  565.             pSize = GetHandleSize( text );
  566.                 
  567.             tState = HGetState( text );
  568.             HLock( text );
  569.             FailOSErr( FSWrite( refNum, &pSize, *text ));
  570.             HSetState( text, tState );
  571.             
  572.             FailOSErr( SetEOF( refNum, pSize ));
  573.             FSClose( refNum );    
  574.             
  575.             // Now save styles:
  576.             
  577.             short        savedStart = (*itsText)->selStart;
  578.             short        savedEnd   = (*itsText)->selEnd;
  579.             
  580.             (*itsText)->selStart = 0;
  581.             (*itsText)->selEnd = (*itsText)->teLength;
  582.             
  583.             StScrpHandle    styles = TEGetStyleScrapHandle( itsText );
  584.             
  585.             (*itsText)->selStart = savedStart;
  586.             (*itsText)->selEnd = savedEnd;
  587.             
  588.             refNum = FSpOpenResFile( &macFile, fsCurPerm );
  589.             
  590.             if( refNum > 0 )
  591.             {
  592.                 Handle        theRes = Get1Resource( 'styl', 128 );
  593.                 
  594.                 if( theRes )
  595.                     RemoveResource( theRes );
  596.                 
  597.                 AddResource((Handle) styles, 'styl', 128, "\p" );
  598.                 WriteResource((Handle) styles );
  599.                 ReleaseResource((Handle) styles );
  600.             }
  601.             
  602.             CloseResFile( refNum );
  603.         }
  604.         catch( OSErr err )
  605.         {
  606.             FSClose( refNum );
  607.             if ( text )
  608.                 HSetState( text, tState );
  609.             throw err;
  610.         }
  611.         ZScroller::SaveFile();
  612.     }
  613. }
  614.  
  615.  
  616. void    ZTextWindow::RecalText()
  617. {
  618.     static Boolean rtInProgress = FALSE;
  619.     
  620.     Rect    content, tBounds, vRect;
  621.     short     textHeight, lineHeight;
  622.     
  623.     if ( ! rtInProgress )
  624.     {
  625.         rtInProgress = TRUE;
  626.         
  627.         Focus();
  628.         SetOrigin( 0, 0 );
  629.         GetContentRect(&content);
  630.         (*itsText)->viewRect = content;
  631.         
  632.         TECalText( itsText );    
  633.         lineHeight = TEGetHeight( 1, 1, itsText );
  634.         textHeight = TEGetHeight( 0, 32767 , itsText );
  635.         
  636.         GetBounds( &tBounds );
  637.         tBounds.bottom = textHeight;
  638.         
  639.         if( wControl == teWindowWidth )
  640.             tBounds.right = tBounds.left + content.right - content.left;
  641.         
  642.         SetBounds( tBounds );
  643.         
  644.         vRect = (*itsText)->destRect;
  645.         OffsetRect( &tBounds, vRect.left - tBounds.left, vRect.top - tBounds.top );
  646.         (*itsText)->destRect = tBounds;
  647.         
  648.         SetScrollAmount( emSpace, lineHeight );
  649.         
  650.         TEAutoView( TRUE, itsText );
  651.         TESelView( itsText );
  652.         TEAutoView( FALSE, itsText );
  653.  
  654.         if ( hasVBar )
  655.             SetControlValue( theVBar, (*itsText)->viewRect.top - (*itsText)->destRect.top );
  656.             
  657.         if ( hasHBar )
  658.             SetControlValue( theHBar, (*itsText)->viewRect.left - (*itsText)->destRect.left );
  659.         
  660.         rtInProgress = FALSE;
  661.     }
  662. }
  663.  
  664.  
  665. void    ZTextWindow::SetWidthControl( TEWidthControl aCtl, short fixWidth )
  666. {
  667.     // this changes the text width control of the view. The default is a fixed width of 255 characters
  668.     // of the max width of the default font. This is quite a good setting for e.g. code listings.
  669.     // However, for text such as Read Me docs, etc, a variable width is probably more useful. In that
  670.     // case the text view is adjusted to the current width of the window.
  671.     
  672.     if ( aCtl != wControl )
  673.     {
  674.         wControl = aCtl;
  675.         
  676.         if ( wControl == teFixedWidth )
  677.             emWidth = fixWidth;
  678.         
  679.         // change the destRect of the text so that it is correct for the behaviour we are
  680.         // selecting.
  681.         
  682.         if ( itsText )
  683.         {
  684.             Rect    r = (*itsText)->destRect;
  685.             Rect    cr;
  686.             
  687.             if ( wControl == teWindowWidth )
  688.             {
  689.                 GetContentRect( &cr );
  690.                 r.right = r.left + cr.right - cr.left;
  691.             }
  692.             else
  693.                 r.right = r.left + ( emSpace * emWidth );
  694.             
  695.             (*itsText)->destRect = r;
  696.                 
  697.             RecalText();
  698.             Draw();
  699.         }
  700.     }
  701. }
  702.  
  703.  
  704. void    ZTextWindow::SetSizeRect( const Rect& szRect )
  705. {
  706.     ZScroller::SetSizeRect( szRect );
  707.     
  708.     // if variable width, allow user to drag window regardless of bounds:
  709.     
  710.     if ( wControl == teWindowWidth )
  711.     {
  712.         sizeRect.right = 10000;
  713.         sizeRect.bottom = 10000;
  714.     }
  715. }
  716.  
  717.  
  718. void    ZTextWindow::TextEditClickLoop()
  719. {
  720.     // perform autoscrolling. This is the default click loop action- you can override it
  721.     // if you want to do something else in the click loop.
  722.     
  723.     Point    mouse;
  724.     Rect    r;
  725.     
  726.     Focus();
  727.     GetMouse( &mouse );
  728.     AutoScroll( mouse );
  729.     SetOrigin( 0, 0 );
  730.     GetContentRect( &r );
  731.     ClipRect( &r );
  732. }
  733.  
  734.  
  735. static pascal Boolean        ztClickproc()
  736. {
  737.     try
  738.     {
  739.         if ( gClickedTE )
  740.             gClickedTE->TextEditClickLoop();
  741.     }
  742.     catch( OSErr err )
  743.     {
  744.     }
  745.     
  746.     return TRUE;
  747. }
  748.